﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace WeiTap.Web
{
    /// <summary>
    /// 反射帮助类
    /// </summary>
    public static class ReflectionHelper
    {
        /// <summary>
        /// 或者成员类型的设置方法的委托
        /// </summary>
        public static Func<MemberInfo, Action<object, object>> GetSetter = m =>
        {
            switch (m.MemberType)
            {
                case MemberTypes.Property:
                    {
                        var property = (PropertyInfo)m;
                        var type = property.PropertyType;
                        if (property.GetIndexParameters().Length > 0)
                        {
                            return null;
                        }
                        var o = Expression.Parameter(typeof(object), "o");

                        if (property.DeclaringType != null)
                        {
                            var cast = Expression.Convert(o, property.DeclaringType);
                            var p = Expression.Property(cast, property);
                            if (property.CanWrite)
                            {
                                var v = Expression.Parameter(typeof(object), "v");
                                var val = Expression.Convert(v, type);
                                var assign = Expression.MakeBinary(ExpressionType.Assign, p, val);
                                var ret = Expression.Convert(assign, typeof(object));
                                return Expression.Lambda<Action<object, object>>(ret, o, v).Compile();
                            }
                            if (property.DeclaringType.IsGenericType &&
                            property.DeclaringType.Name.StartsWith("<>f__AnonymousType")) //匿名类型
                            {
                                var fieldName = $"<{property.Name}>i__Field";
                                m = property.DeclaringType.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
                                if (m != null)
                                {
                                    goto case MemberTypes.Field;
                                }
                            }
                        }

                    }
                    break;
                case MemberTypes.Constructor:
                    break;
                case MemberTypes.Event:
                    break;
                case MemberTypes.Field:
                    {
                        var field = (FieldInfo)m;
                        if (field.IsInitOnly)
                        {
                            return field.SetValue;
                        }
                        var type = field.FieldType;
                        var o = Expression.Parameter(typeof(object), "o");

                        if (field.DeclaringType != null)
                        {
                            var cast = Expression.Convert(o, field.DeclaringType);
                            var p = Expression.Field(cast, field);
                            if (field.IsLiteral == false)
                            {
                                var v = Expression.Parameter(typeof(object), "v");
                                var val = Expression.Convert(v, type);
                                var assign = Expression.MakeBinary(ExpressionType.Assign, p, val);
                                var ret2 = Expression.Convert(assign, typeof(object));
                                return Expression.Lambda<Action<object, object>>(ret2, o, v).Compile();
                            }
                        }
                    }
                    break;
                case MemberTypes.Method:
                    break;
                case MemberTypes.TypeInfo:
                    break;
                case MemberTypes.Custom:
                    break;
                case MemberTypes.NestedType:
                    break;
                case MemberTypes.All:
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            return null;
        };
        /// <summary>
        /// 
        /// </summary>
        public static Func<MemberInfo, Func<object, object>> GetGetter = m =>
        {
            switch (m.MemberType)
            {
                case MemberTypes.Constructor:
                    break;
                case MemberTypes.Event:
                    break;
                case MemberTypes.Field:
                    {
                        var field = (FieldInfo)m;
                        var o = Expression.Parameter(typeof(object), "o");

                        if (field.DeclaringType != null)
                        {
                            var cast = Expression.Convert(o, field.DeclaringType);
                            var p = Expression.Field(cast, field);
                            var ret = Expression.Convert(p, typeof(object));
                            return Expression.Lambda<Func<object, object>>(ret, o).Compile();
                        }
                    }
                    break;
                case MemberTypes.Method:
                    break;
                case MemberTypes.Property:
                    {
                        var property = (PropertyInfo)m;
                        if (property.GetIndexParameters().Length > 0)//带索引类型的属性
                        {
                            return null;
                        }
                        var param = Expression.Parameter(typeof(object), "o");
                        if (property.DeclaringType != null)
                        {
                            var cast = Expression.Convert(param, property.DeclaringType);
                            var p = Expression.Property(cast, property);
                            if (property.CanRead)
                            {
                                var ret = Expression.Convert(p, typeof(object));
                                return Expression.Lambda<Func<object, object>>(ret, param).Compile();
                            }
                        }
                    }
                    break;
                case MemberTypes.TypeInfo:
                    break;
                case MemberTypes.Custom:
                    break;
                case MemberTypes.NestedType:
                    break;
                case MemberTypes.All:
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            return null;
        };
    }
}
